<?php
/**
 *  WB_StorableClass class
 *  [Mememto pattern]
 *  1) Provides a base class capable of:
 *  - creating an object from its XML representation
 *  - creating a representation of an object with XML
 *  2) Skips the variables beginning with an underscore '_'
 *
 *  @author Jean-Lou Dupont
 *  @version @@package-version@@
 */
//{{include
require_once 'web-bloks/BaseClass/BaseClass.php';
//include}}
 abstract class WB_StorableClass 
 	extends WB_BaseClass
 {
 	/**
 	 * Constructor
 	 * Derived classes from this class must pass
 	 * the $class and $params parameters.
 	 * 
 	 * @return 
 	 * @param $class Object
 	 * @param $params Object[optional]
 	 */
 	public function __construct( $params = null )
	{
		parent::__construct( $params );
	}
	/**
	 * Default implementation (inherited from BaseClass)
	 * 
	 * @return 
	 */
	public function getClassPath()
	{
		return 'web-bloks/StorableClass';
	}

 // ======================================================
 // Interface 
 // ======================================================
	/**
	 *  toXML
	 *  Returns an XML based representaton of the object
	 *  
	 * @return string XML representation of the object
	 */
	public function toXML()
	{
		$props = get_object_vars( $this );

		if (empty( $props ))
			return null;

		$version = $this->getClassVersion();
		$path = $this->getClassPath();
		$class = get_class( $this );
		
		$xmlstr = $this->buildXMLheader( $class, $path, $version );
			
		foreach( $props as $name => &$value )
		{
			// remove the vars starting with an underscore
			if ($name[0] == '_' )
				continue;
				
			// figure out the property type 
			$type = gettype( $this->$name );
			$xmlstr .= $this->buildXMLproperty( $type, $name, $value );
		}
		
		$xmlstr .= $this->buildXMLfooter();
		
		return $xmlstr;
	}

	/**
	 * fromXML (factory method)
	 * Creates an object from an XML representation.
	 * 
	 * @return object
	 * @param $xmlstr string
	 */
	public static function & fromXML( &$xmlstr )
	{
		try 
		{
			$xml = @new SimpleXMLElement( $xmlstr );
		} 
		catch(Exception $e)
		{
			$msg = __CLASS__.":".$e->getMessage();
			$code= $e->getCode();
			throw new Exception( $msg, $code );
		}
		$class = (string) $xml->class;
		$path = (string) $xml->path;
		
		if ( !class_exists( $class ))
			@require_once $path;
		
		if ( !class_exists( $class ))
			throw new Exception( ': missing class "'.$class.'"' );
		
		$obj = new $class;
		
		$properties = $xml->props->prop;

		foreach( $properties as $property )
			$obj->setPropertyFromXML( $obj, $property );

		return $obj;
	}

 // ======================================================
 // Protected methods
 // ======================================================
	
	/**
	 * Builds the XML header
	 * 
	 * @return string
	 * @param $class string class name
	 * @param $path string path to the class definition file
	 * @param $version string version of the class file
	 */
	protected function buildXMLheader( &$class, &$path, &$version )
	{
		$xmlstr = <<<EOF
			<object>		
				<class>$class</class>
				<path>$path</path>
				<version>$version</version>
				<props>\n
EOF;
		return $xmlstr;
	}
	/**
	 * Builds the XML footer
	 * @return string
	 */
	protected function buildXMLfooter()
	{
		$xmlstr = <<<EOF
				</props>
			</object>
EOF;
		return $xmlstr;
	}
	/**
	 * Builds an XML property
	 * 
	 * @return 
	 * @param $type primitive type
	 * @param $key key name of the property
	 * @param $value value assigned to the key
	 */
	protected function buildXMLproperty( &$type, &$key, &$value )
	{
		$method = "${type}_toXML";
		return $this->$method( $key, $value );		
	}
	/**
	 * NULL
	 */
	protected function NULL_toXML( &$key, &$value )
	{
		return $this->simpleToXml( 'null', $key, $value );
	}	
	/**
	 * Builds a simple XML line
	 * @return 
	 */
	protected function simpleToXml( $type, &$key, &$value )
	{
		$xmlstr = <<<EOF
			<prop>
				<name>$key</name>
				<value type='$type'>$value</value>
			</prop>
EOF;
		return $xmlstr;	
	}
	/**
	 * Builds a 'string' property
	 * 
	 * @return string XML string 
	 * @param 
	 */
	protected function string_toXML( &$key, &$value )
	{
		return $this->simpleToXml( 'string', 
									&$key, 
									base64_encode( $value ) );
	}
	/**
	 * Builds an 'boolean' property
	 * 
	 * @return 
	 * @param $key Object
	 * @param $value Object
	 */
	protected function boolean_toXML( &$key, &$value )
	{
		return $this->simpleToXml( 'boolean', $key, $value );
	}
	/**
	 * Builds an 'integer' property
	 * 
	 * @return 
	 * @param $key Object
	 * @param $value Object
	 */
	protected function integer_toXML( &$key, &$value )
	{
		return $this->simpleToXml( 'integer', $key, $value );
	}
	/**
	 * Builds a 'double' property
	 * 
	 * @return 
	 * @param $key Object
	 * @param $value Object
	 */
	protected function double_toXML( &$key, &$value )
	{
		return $this->simpleToXml( 'double', $key, $value );
	}
	/**
	 * Builds an 'array' property
	 * $value is an array.
	 * 
	 * @return string XML string
	 * @param $key name of the object
	 * @param $value value of the object
	 */
	protected function array_toXML( &$name, &$value )
	{
		assert( is_array( $value ) );
		$xmlstr = '';
		
		foreach( $value as $key => $val )
		{
			$type = gettype( $val );
			$xmlstr .= $this->buildXMLproperty( $type, $key, $val );
		}
		
		return $this->simpleToXml( 'array', $name, $xmlstr );
	}
	
	/**
	 * Sets the $obj property based on the provided
	 * SimpleXML object.
	 * 
	 * @param $obj Object
	 * @param $property Object
	 */
	protected function setPropertyFromXML( &$obj, &$property )
	{
		$name = (string) $property->name;
		$type = (string) $property->value['type'];
		
		$method = "${type}_setFromXML";
		
		return $this->$method( $obj, $name, $property );		
	}
	/**
	 * Simple_setFromXML
	 * Used to set all primitive properties of an object.
	 * 
	 * @param $obj object
	 * @param $name
	 * @param $property SimpleXML
	 */
	protected function simple_setFromXML( &$obj, &$name, &$property )
	{
		$obj->$name = $property->value;		
	}
	/**
	 * NULL
	 */
	protected function NULL_setFromXML( &$obj, &$name, &$property )
	{
		$obj->$name = null;
	}	
	/**
	 * Sets a 'string' property of an object.
	 * 
	 * @param $obj Object
	 * @param $name string
	 * @param $property Object
	 */
	protected function string_setFromXML( &$obj, &$name, &$property )
	{
		$obj->$name = (string) base64_decode( $property->value );
	}
	/**
	 * Sets a 'boolean' property of an object.
	 * 
	 * @param $obj Object
	 * @param $name string
	 * @param $property Object
	 */
	protected function boolean_setFromXML( &$obj, &$name, &$property )
	{
		$obj->$name = (boolean) $property->value;		
	}
	/**
	 * Sets a 'double' property of an object.
	 * 
	 * @param $obj Object
	 * @param $name string
	 * @param $property Object
	 */
	protected function double_setFromXML( &$obj, &$name, &$property )
	{
		$obj->$name = (double) $property->value;		
	}
	/**
	 * Sets an 'integer' property of an object.
	 * 
	 * @param $obj Object
	 * @param $name string
	 * @param $property Object
	 */
	protected function integer_setFromXML( &$obj, &$name, &$property )
	{
		$obj->$name = (integer) $property->value;		
	}
	/**
	 * 
	 * @param $obj Object
	 * @param $name string
	 * @param $property Object
	 */
	protected function array_setFromXML( &$obj, &$name, &$properties )
	{
		foreach( $properties->property as $property )
			$this->setPropertyFromXML( $obj, $property );
	}

 } // end class
